/**
 * 
 */
package edu.rice.myxo.olexii1;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D.Double;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

import javax.imageio.ImageIO;

import org.omg.SendingContext.RunTime;

import edu.rice.myxo.olexii1.MyxoAgent;
import edu.rice.myxo.utils.MathUtils;

/**
 * @author Haiyang Zhang
 */
public class AgentSpace implements Cloneable {

	/**
	 * Initiallize agent space Agent space is always a rectangle area start with
	 * (0,0) and with worldx+2*Global.maxNeighborDis in width and
	 * worldy+2*global.maxNeighborDis in height WITH MIRROR AGENTS!
	 * 
	 * Without mirror agents, the world is (0,0), width of worldx and height of
	 * worldy
	 * 
	 */
	public AgentSpace() {
		worldX = Global.worldX;
		worldY = Global.worldY;
		gridSize = Global.gridSize;
		// turn on mirror agent
		// numGridX = (int) ((worldX + 2 * Global.maxNeighborDis) / gridSize);
		// numGridY = (int) ((worldY + 2 * Global.maxNeighborDis) / gridSize);
		// turn off mirror agent
		numGridX = (int) (worldX / gridSize);
		numGridY = (int) (worldY / gridSize);
		pixSize = Global.pixSize;
		setNumPixX((int) (worldX / pixSize));
		setNumPixY((int) (worldY / pixSize));
		gridList = new Grid[numGridX][numGridY];
	}

	public void setAgentList(ArrayList<MyxoAgent> agentList) {
		this.agentList = agentList;
		for (int i = 0; i < numGridX; i++) {
			for (int j = 0; j < numGridY; j++) {
				gridList[i][j] = new Grid(i, j, (double) i * gridSize,
						(double) j * gridSize);
			}
		}
		for (int i = 0; i < agentList.size(); i++) {
			// mirror agents
			// int gridX = agentList.get(i).getGridX()
			// + (int) (Global.maxNeighborDis / gridSize);
			// int gridY = agentList.get(i).getGridY()
			// + (int) (Global.maxNeighborDis / gridSize);
			int gridX = agentList.get(i).getGridX();
			int gridY = agentList.get(i).getGridY();
			gridList[gridX][gridY].agentList.add(agentList[i]);
			// mirror agents
			// gridX = agentList.get(i).getHeadGridX()
			// + (int) (Global.maxNeighborDis / gridSize);
			// gridY = agentList.get(i).getHeadGridY()
			// + (int) (Global.maxNeighborDis / gridSize);
			gridX = agentList.get(i).getHeadGridX();
			gridY = agentList.get(i).getHeadGridY();
			gridList[gridX][gridY].agentHeadList.add(agentList.get(i));

			// mirror agents
			// gridX = agentList.get(i).getTailGridX()
			// + (int) (Global.maxNeighborDis / gridSize);
			// gridY = agentList.get(i).getTailGridY()
			// + (int) (Global.maxNeighborDis / gridSize);
			gridX = agentList.get(i).getTailGridX();
			gridY = agentList.get(i).getTailGridY();

			gridList[gridX][gridY].agentTailList.add(agentList.get(i));

		}

		// mirror agents
		// for (int i = 0; i < Global.mirrorAgents.size(); i++) {
		// int gridX = Global.mirrorAgents.get(i).getGridX()
		// + (int) (Global.maxNeighborDis / gridSize);
		// int gridY = Global.mirrorAgents.get(i).getGridY()
		// + (int) (Global.maxNeighborDis / gridSize);
		// gridList[gridX][gridY].agentList.add(Global.mirrorAgents.get(i));
		// gridX = Global.mirrorAgents.get(i).getHeadGridX()
		// + (int) (Global.maxNeighborDis / gridSize);
		// gridY = Global.mirrorAgents.get(i).getHeadGridY()
		// + (int) (Global.maxNeighborDis / gridSize);
		// if (gridX >= 0 && gridX < numGridX && gridY >= 0
		// && gridY < numGridY) {
		// gridList[gridX][gridY].agentHeadList.add(agentList.get(i));
		// }
		// gridX = Global.mirrorAgents.get(i).getTailGridX()
		// + (int) (Global.maxNeighborDis / gridSize);
		// gridY = Global.mirrorAgents.get(i).getTailGridY()
		// + (int) (Global.maxNeighborDis / gridSize);
		// if (gridX >= 0 && gridX < numGridX && gridY >= 0
		// && gridY < numGridY) {
		// gridList[gridX][gridY].agentTailList.add(agentList.get(i));
		// }
		// }
	}

	public void updateAgentSpace() {
		for (int i = 0; i < numGridX; i++) {
			for (int j = 0; j < numGridY; j++) {
				gridList[i][j].agentHeadList.clear();
				gridList[i][j].agentList.clear();
				gridList[i][j].agentTailList.clear();
				if (Global.currTime % Global.outputDt == 0) {
					gridList[i][j].agentList = null;
					gridList[i][j].agentList = new ArrayList<MyxoAgent>();
					gridList[i][j].agentHeadList = null;
					gridList[i][j].agentHeadList = new ArrayList<MyxoAgent>();
					gridList[i][j].agentTailList = null;
					gridList[i][j].agentTailList = new ArrayList<MyxoAgent>();
				}
			}
		}
		for (int i = 0; i < agentList.size(); i++) {
			agentList.get(i).updateAgentState();
			int[] gridXY = toSpaceGrid(agentList.get(i).getGridX(), agentList
					.get(i).getGridY());
			gridList[gridXY[0]][gridXY[1]].agentList.add(agentList.get(i));
			gridXY = toSpaceGrid(agentList.get(i).getHeadGridX(), agentList
					.get(i).getHeadGridY());
			if (gridXY[0] >= 0 && gridXY[0] < numGridX && gridXY[1] >= 0
					&& gridXY[1] < numGridY) {
				gridList[gridXY[0]][gridXY[1]].agentHeadList.add(agentList
						.get(i));
			}
			gridXY = toSpaceGrid(agentList.get(i).getTailGridX(), agentList
					.get(i).getTailGridY());
			if (gridXY[0] >= 0 && gridXY[0] < numGridX && gridXY[1] >= 0
					&& gridXY[1] < numGridY) {
				gridList[gridXY[0]][gridXY[1]].agentTailList.add(agentList
						.get(i));
			}
		}

		// mirror agents
		// for (int i = 0; i < Global.mirrorAgents.size(); i++) {
		// Global.mirrorAgents.get(i).updateAgentState();
		// int[] gridXY = toSpaceGrid(Global.mirrorAgents.get(i).getGridX(),
		// Global.mirrorAgents.get(i).getGridY());
		// gridList[gridXY[0]][gridXY[1]].agentList.add(Global.mirrorAgents
		// .get(i));
		// gridXY = toSpaceGrid(Global.mirrorAgents.get(i).getHeadGridX(),
		// Global.mirrorAgents.get(i).getHeadGridY());
		// if (gridXY[0] >= 0 && gridXY[0] < numGridX && gridXY[1] >= 0
		// && gridXY[1] < numGridY) {
		// gridList[gridXY[0]][gridXY[1]].agentHeadList
		// .add(Global.mirrorAgents.get(i));
		// }
		// gridXY = toSpaceGrid(Global.mirrorAgents.get(i).getTailGridX(),
		// Global.mirrorAgents.get(i).getTailGridY());
		// if (gridXY[0] >= 0 && gridXY[0] < numGridX && gridXY[1] >= 0
		// && gridXY[1] < numGridY) {
		// gridList[gridXY[0]][gridXY[1]].agentTailList
		// .add(Global.mirrorAgents.get(i));
		// }
		// }
	}

	/**
	 * Get the local agents in a rectangle area centered with (centerX, centerY)
	 * and with range +-range the rectangle area is
	 * (centerX+-range,centerY+-range) range must < maxNeighborDis
	 * 
	 * @param centerX
	 * @param centerY
	 * @param range
	 * @return
	 */
	public ArrayList<MyxoAgent> getAgentList(double centerX, double centerY,
			double range) {
		ArrayList<MyxoAgent> localAgentList = new ArrayList<MyxoAgent>();
		// if (range < gridSize) {
		// range = gridSize;
		// }

		// mirror agents
		// int startX = (int) ((centerX - range + Global.maxNeighborDis) /
		// gridSize);
		// int startY = (int) ((centerY - range + Global.maxNeighborDis) /
		// gridSize);
		// int endX = (int) ((centerX + range + Global.maxNeighborDis) /
		// gridSize);
		// int endY = (int) ((centerY + range + Global.maxNeighborDis) /
		// gridSize);
		// startX = startX >= 0 ? startX : 0;
		// startY = startY >= 0 ? startY : 0;
		// endX = endX < numGridX ? endX : numGridX - 1;
		// endY = endY < numGridY ? endY : numGridY - 1;

		
		
		int numGrid = (int) (2 * range / gridSize) + 1;
		int[] xGrid = new int[numGrid];
		int[] yGrid = new int[numGrid];
		int temp;
		for (int i = 0; i < numGrid; i++) {
			temp = (int) ((centerX - range) / gridSize) + i;
			temp = (temp + numGridX) % numGridX;
			xGrid[i] = temp;
			temp = (int) ((centerY - range) / gridSize) + i;
			temp = (temp + numGridY) % numGridY;
			yGrid[i] = temp;
		}
	
		
		// for (int i = startX; i <= endX; i++) {
		// for (int j = startY; j <= endY; j++) {
		// localAgentList.addAll(gridList[i][j].agentList);
		// // try{
		// // localAgentList.addAll(gridList[i][j].agentList);
		// // }catch(ArrayIndexOutOfBoundsException e){
		// // System.out.println("*****************************");
		// // System.out.println(gridList[i][j].agentList.get(0).getID());
		// // System.out.println(gridList.length + " " + gridList[0].length
		// // + " " + i + " " + j);
		// // }
		// }
		// }

		int numAgents = 0;
		for (int i = 0; i < numGrid; i++) {
			for (int j = 0; j < numGrid; j++) {
				for (int k = 0; k < gridList[xGrid[i]][yGrid[j]].agentList
						.size(); k++) {
					double dx = centerX
							- gridList[xGrid[i]][yGrid[j]].agentList.get(k)
									.getXOld();
					double dy = centerY
							- gridList[xGrid[i]][yGrid[j]].agentList.get(k)
									.getYOld();
					dx -= (double) ((int) (dx / Global.worldX + 0.5))
							* Global.worldX;
					dy -= (double) ((int) (dy / Global.worldY + 0.5))
							* Global.worldY;
					double dis = Math.sqrt(dx * dx + dy * dy);
					if (dis < range) {
						localAgentList
								.add(gridList[xGrid[i]][yGrid[j]].agentList
										.get(k));
					}
					numAgents += gridList[xGrid[i]][yGrid[j]].agentList.size();
				}
			}
		}

//		if (numAgents != 0)
//			System.out.println((double) localAgentList.size()
//					/ (double) numAgents);

		return localAgentList;
	}

	/**
	 * Get the local agent heads in a rectangle area centered with (centerX,
	 * centerY) and with range +-range the rectangle area is
	 * (centerX+-range,centerY+-range) range must < maxNeighborDis
	 * 
	 * @param centerX
	 * @param centerY
	 * @param range
	 * @return
	 */
	public ArrayList<MyxoAgent> getHeadAgentList(double centerX,
			double centerY, double range) {
		ArrayList<MyxoAgent> localHeadAgentList = new ArrayList<MyxoAgent>();
		// if (range < gridSize) {
		// range = gridSize;
		// }

		// mirror agents
		// int startX = (int) ((centerX - range + Global.maxNeighborDis) /
		// gridSize);
		// int startY = (int) ((centerY - range + Global.maxNeighborDis) /
		// gridSize);
		// int endX = (int) ((centerX + range + Global.maxNeighborDis) /
		// gridSize);
		// int endY = (int) ((centerY + range + Global.maxNeighborDis) /
		// gridSize);

		if (centerX > worldX || centerX < 0) {
			centerX = (centerX + worldX) % worldX;
		}
		if (centerY > worldY || centerY < 0) {
			centerY = (centerY + worldY) % worldY;
		}
		int numGrid = (int) (2 * range / gridSize) + 1;
		int[] xGrid = new int[numGrid];
		int[] yGrid = new int[numGrid];
		int temp;
		for (int i = 0; i < numGrid; i++) {
			temp = (int) ((centerX - range) / gridSize) + i;
			temp = (temp + numGridX) % numGridX;
			xGrid[i] = temp;
			temp = (int) ((centerY - range) / gridSize) + i;
			temp = (temp + numGridY) % numGridY;
			yGrid[i] = temp;
		}
		for (int i = 0; i < numGrid; i++) {
			for (int j = 0; j < numGrid; j++) {
				for (int k = 0; k < gridList[xGrid[i]][yGrid[j]].agentHeadList
						.size(); k++) {
					double dx = centerX
							- gridList[xGrid[i]][yGrid[j]].agentHeadList.get(k)
									.getHeadX();
					double dy = centerX
							- gridList[xGrid[i]][yGrid[j]].agentHeadList.get(k)
									.getHeadY();
					double dis = Math.sqrt(dx * dx + dy * dy);
					if (dis < range) {
						localHeadAgentList
								.add(gridList[xGrid[i]][yGrid[j]].agentHeadList
										.get(k));
					}
				}
			}
		}
		return localHeadAgentList;
	}
	
	public ArrayList<MyxoAgent> getAgentListS(double centerX, double centerY,
			double range) {
		ArrayList<MyxoAgent> localAgentList = new ArrayList<MyxoAgent>();
		// if (range < gridSize) {
		// range = gridSize;
		// }

		// mirror agents
		// int startX = (int) ((centerX - range + Global.maxNeighborDis) /
		// gridSize);
		// int startY = (int) ((centerY - range + Global.maxNeighborDis) /
		// gridSize);
		// int endX = (int) ((centerX + range + Global.maxNeighborDis) /
		// gridSize);
		// int endY = (int) ((centerY + range + Global.maxNeighborDis) /
		// gridSize);
		// startX = startX >= 0 ? startX : 0;
		// startY = startY >= 0 ? startY : 0;
		// endX = endX < numGridX ? endX : numGridX - 1;
		// endY = endY < numGridY ? endY : numGridY - 1;

		if (centerX >= worldX || centerX < 0) {
			centerX = (centerX + worldX) % worldX;
		}
		if (centerY >= worldY || centerY < 0) {
			centerY = (centerY + worldY) % worldY;
		}
		int startX = (int) ((centerX - range) / gridSize);
		int startY = (int) ((centerY - range) / gridSize);
		int endX = (int) ((centerX + range) / gridSize);
		int endY = (int) ((centerY + range) / gridSize);
		startX = (startX >= 0 ? startX : 0);
		startY = (startY >= 0 ? startY : 0);
		endX = (endX < numGridX ? endX : numGridX - 1);
		endY = (endY < numGridY ? endY : numGridY - 1);
		for (int i = startX; i <= endX; i++) {
			for (int j = startY; j <= endY; j++) {
				localAgentList.addAll(gridList[i][j].agentList);
				// try{
				// localAgentList.addAll(gridList[i][j].agentList);
				// }catch(ArrayIndexOutOfBoundsException e){
				// System.out.println("*****************************");
				// System.out.println(gridList[i][j].agentList.get(0).getID());
				// System.out.println(gridList.length + " " + gridList[0].length
				// + " " + i + " " + j);
				// }
			}
		}

		return localAgentList;
	}

	/**
	 * Get the local agent heads in a rectangle area centered with (centerX,
	 * centerY) and with range +-range the rectangle area is
	 * (centerX+-range,centerY+-range) range must < maxNeighborDis
	 * 
	 * @param centerX
	 * @param centerY
	 * @param range
	 * @return
	 */
	public synchronized ArrayList<MyxoAgent> getHeadAgentListS(double centerX,
			double centerY, double range) {
		ArrayList<MyxoAgent> localHeadAgentList = new ArrayList<MyxoAgent>();
		// if (range < gridSize) {
		// range = gridSize;
		// }

		// mirror agents
		// int startX = (int) ((centerX - range + Global.maxNeighborDis) /
		// gridSize);
		// int startY = (int) ((centerY - range + Global.maxNeighborDis) /
		// gridSize);
		// int endX = (int) ((centerX + range + Global.maxNeighborDis) /
		// gridSize);
		// int endY = (int) ((centerY + range + Global.maxNeighborDis) /
		// gridSize);

		if (centerX > worldX || centerX < 0) {
			centerX = (centerX + worldX) % worldX;
		}
		if (centerY > worldY || centerY < 0) {
			centerY = (centerY + worldY) % worldY;
		}
		int startX = (int) ((centerX - range) / gridSize);
		int startY = (int) ((centerY - range) / gridSize);
		int endX = (int) ((centerX + range) / gridSize);
		int endY = (int) ((centerY + range) / gridSize);
		startX = startX >= 0 ? startX : 0;
		startY = startY >= 0 ? startY : 0;
		endX = endX < numGridX ? endX : numGridX - 1;
		endY = endY < numGridY ? endY : numGridY - 1;
		for (int i = startX; i <= endX; i++) {
			for (int j = startY; j <= endY; j++) {
				localHeadAgentList.addAll(gridList[i][j].agentHeadList);

			}
		}
		return localHeadAgentList;
	}

	
	

	/**
	 * Output agents as lines within region (startX, startY, width, height) The
	 * output file is a png image file. Ratio is the result image size/actual
	 * image size.
	 * 
	 * @param startX
	 * @param startY
	 * @param width
	 * @param height
	 * @param time
	 */
	public void outputAgentSpace(double startX, double startY, double width,
			double height, double time) {
		double ratio = 1;
		int imWidth = (int) (width * ratio);
		int imHeight = (int) (height * ratio);
		BufferedImage spaceShot = new BufferedImage(imWidth, imHeight,
				BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g2 = spaceShot.createGraphics();
		g2.setColor(Color.white);
		g2.fill(new Rectangle2D.Double(0, 0, imWidth, imHeight));
		g2.setColor(Color.black);
		for (int i = 0; i < agentList.size(); i += Global.outputNumRatio) {
			if (agentList.get(i).isTrackLabel()) {
				g2.setColor(Color.red);
				double r = agentList.get(i).getCellLength() / 3;
				g2.draw(new Ellipse2D.Double(agentList.get(i).getX() - r,
						agentList.get(i).getY() - r, 2 * r, 2 * r));
			}
			g2.setColor(Color.black);
			if (Global.DEBUG) {
				if (agentList.get(i).isIfAttached()) {
					g2.setColor(Color.GREEN);
				}
				if (agentList.get(i).isIfAttaching()) {
					g2.setColor(Color.red);
					double r = Global.maxNeighborDis;
					g2.draw(new Ellipse2D.Double(agentList.get(i).getHeadX()
							- r, agentList.get(i).getHeadY() - r, 2 * r, 2 * r));
				}
			}
			// Line2D.Double ln = new Line2D.Double(agentList.get(i).getTailX()
			// - (1 - 1) * agentList.get(i).getCellLength()
			// * Math.cos(agentList.get(i).getDir()) / 2, agentList.get(i)
			// .getTailY()
			// - (1 - 1)
			// * agentList.get(i).getCellLength()
			// * Math.sin(agentList.get(i).getDir()) / 2, agentList.get(i)
			// .getHeadX()
			// + (1 - 1)
			// * agentList.get(i).getCellLength()
			// * Math.cos(agentList.get(i).getDir()) / 2, agentList.get(i)
			// .getHeadY()
			// + (1 - 1)
			// * agentList.get(i).getCellLength()
			// * Math.sin(agentList.get(i).getDir()));
			Line2D.Double ln = new Line2D.Double(agentList.get(i).getX()
					* ratio - agentList.get(i).getCellLength() * ratio
					* Math.cos(agentList.get(i).getDir()) / 2, agentList.get(i)
					.getY()
					* ratio
					- agentList.get(i).getCellLength()
					* ratio
					* Math.sin(agentList.get(i).getDir()) / 2, agentList.get(i)
					.getX()
					* ratio
					+ agentList.get(i).getCellLength()
					* ratio
					* Math.cos(agentList.get(i).getDir()) / 2, agentList.get(i)
					.getY()
					* ratio
					+ agentList.get(i).getCellLength()
					* ratio
					* Math.sin(agentList.get(i).getDir()) / 2);
			g2.draw(ln);
			// if (agentList.get(i).isCOLLIDE()) {
			// g2.setColor(Color.black);
			// }
		}
		String fileName = Global.imageDirName + "//" + "cellspace" + (int) time
				+ ".png";
		try {
			File file = new File(fileName);
			ImageIO.write(spaceShot, "png", file);
		} catch (IOException e) {
		}
	}

	public void outputDemo(double startX, double startY, double width,
			double height, double time) {
		BufferedImage demoImage = new BufferedImage((int) width, (int) height,
				BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g2 = demoImage.createGraphics();
		g2.setColor(Color.white);
		g2.fill(new Rectangle2D.Double(0, 0, (int) width, (int) height));
		g2.setColor(Color.black);
		for (int i = 0; i < agentList.size(); i++) {
			if (agentList.get(i).isCOLLIDE()) {
				g2.setColor(Color.red);
			}
			g2.draw(new Rectangle2D.Double(agentList.get(i).getX()
					- Global.cellLength / 2, agentList.get(i).getY()
					- Global.cellWidth / 2, Global.cellLength, Global.cellWidth));
			g2.setColor(Color.black);
			agentList.get(i).setCOLLIDE(false);
		}
		String fileName = Global.imageDirName + "//" + "demoimage" + (int) time
				+ ".png";
		try {
			File file = new File(fileName);
			ImageIO.write(demoImage, "png", file);
		} catch (IOException e) {

		}
	}

	/**
	 * Output agent space within range (startX,startX + width, startY, startY +
	 * height) Each cell is represented by numPts pixels. the center grid is
	 * decided by (x,y)
	 * 
	 * @param startX
	 * @param startY
	 * @param width
	 * @param height
	 * @throws IOException
	 */
	public void outputAgentSpace(double startX, double startY, double width,
			double height) throws IOException {
		String fileName = Global.dirName + "//" + "cellspace"
				+ (int) Global.currTime + ".txt";
		FileWriter fw = new FileWriter(fileName);
		PrintWriter pw = new PrintWriter(fw);
		int startPixX = (int) (startX / pixSize);
		int startPixY = (int) (startY / pixSize);
		int pixWidth = (int) (width / pixSize);
		int pixHeight = (int) (height / pixSize);
		if (Global.DEBUG) {
			System.out.println(pixWidth + " " + pixHeight);
		}
		int[][] spacePixs = new int[pixWidth][pixHeight];
		for (int i = 0; i < pixWidth; i++) {
			for (int j = 0; j < pixHeight; j++) {
				spacePixs[i][j] = 0;
			}
		}
		double lengthStepSize = Global.cellLength / (double) pixSize;
		if (lengthStepSize < 1) {
			lengthStepSize = 1;
		}
		lengthStepSize = (int) lengthStepSize;
		Rectangle2D gridRec = new Rectangle2D.Double();
		for (int i = 0; i < agentList.size(); i++) {
			Line2D agentLine = new Line2D.Double();
			agentLine.setLine(agentList.get(i).getTailX(), agentList.get(i)
					.getTailY(), agentList.get(i).getHeadX(), agentList.get(i)
					.getHeadY());
			int centerGridX = (int) (agentList.get(i).getX() / pixSize);
			int centerGridY = (int) (agentList.get(i).getY() / pixSize);
			int lowGridX = centerGridX - (int) (lengthStepSize / 2);
			int lowGridY = centerGridY - (int) (lengthStepSize / 2);
			int highGridX = centerGridX + (int) (lengthStepSize / 2);
			int highGridY = centerGridY + (int) (lengthStepSize / 2);
			lowGridX = lowGridX < startPixX ? startPixX : lowGridX;
			lowGridY = lowGridY < startPixY ? startPixY : lowGridY;
			highGridX = highGridX > startPixX + pixWidth ? startPixX + pixWidth
					: highGridX;
			highGridY = highGridY > startPixY + pixHeight ? startPixY
					+ pixHeight : highGridY;
			for (int x = lowGridX; x < highGridX; x++) {
				for (int y = lowGridY; y < highGridY; y++) {
					gridRec.setRect((double) x, (double) y, (double) pixSize,
							(double) pixSize);
					if (agentLine.intersects(gridRec)) {
						spacePixs[x][y]++;
					}
				}
			}

		}
		for (int i = 0; i < pixWidth; i++) {
			for (int j = 0; j < pixHeight; j++) {
				pw.print(spacePixs[i][j] + " ");
			}
			pw.println();
		}
		pw.close();
		fw.close();

	}

	public void outputDensity(double startX, double startY, double width,
			double height) {
		String fileName = Global.dirName + "//" + "density"
				+ (int) Global.currTime + ".txt";
		try {
			FileWriter fw = new FileWriter(fileName);
			PrintWriter pw = new PrintWriter(fw);
			for (int i = 0; i < numGridX; i++) {
				for (int j = 0; j < numGridY; j++) {
					pw.print(gridList[i][j].agentList.size() + " ");
				}
				pw.println();
			}
			pw.close();
			fw.close();
		} catch (IOException e) {
		}
	}

	// Output the cumulative density along x axis (cumulate the density along y
	// axis)
	public void outputAlongX() {
		String fileName = Global.dirName + "//" + "densityx"
				+ (int) Global.currTime + ".txt";
		try {
			FileWriter fw = new FileWriter(fileName);
			PrintWriter pw = new PrintWriter(fw);
			int startX = (int) Global.maxNeighborDis + 1;
			int endX = startX + (int) (worldX / gridSize);
			int startY = (int) Global.maxNeighborDis + 1 + 100;
			// int endY = startY + (int)(worldX / gridSize);
			int endY = startY + 50;
			double[] densityArray = new double[endX - startX];
			for (int i = 0; i < densityArray.length; i++) {
				for (int j = startY; j < endY; j++) {
					densityArray[i] += (double) gridList[i][j].agentList.size();
				}
			}
			for (int i = 0; i < densityArray.length; i++) {
				// pw.print(densityArray[i] + " ");
				// if(0 == i%20){
				// pw.println();
				// }
				pw.println(densityArray[i]);
			}
			pw.close();
			fw.close();
		} catch (IOException e) {
		}
	}

	/**
	 * Can output one directional travelign cell density. To run on cluster
	 * (Sugar), just output one direction (or the folder will be overload...)
	 */
	public void outputOneDirectionTravelingDensity() {
		String fileNameLeft = Global.dirName + "//" + "densityleft"
				+ (int) Global.currTime + ".txt";
		// String fileNameRight = Global.dirName + "//" + "densityright"
		// + (int) Global.currTime + ".txt";
		int[][] denLeft = new int[(int) worldX][(int) worldY];
		// int[][] denRight = new int[(int) worldX][(int) worldY];
		for (int i = 0; i < denLeft.length; i++) {
			denLeft[i] = new int[(int) worldY];
			// denRight[i] = new int[(int) worldY];
		}
		for (int i = 0; i < agentList.size(); i++) {
			int agentGridx = (int) agentList.get(i).getX();
			int agentGridy = (int) agentList.get(i).getY();
			if (agentList.get(i).getDir() > Math.PI / 2
					&& agentList.get(i).getDir() < 3 * Math.PI / 2) {
				denLeft[agentGridx][agentGridy] += 1;
			} else {
				// denRight[agentGridx][agentGridy] += 1;
			}
		}
		try {
			FileWriter lfw = new FileWriter(fileNameLeft);
			// FileWriter rfw = new FileWriter(fileNameRight);
			PrintWriter lpw = new PrintWriter(lfw);
			// PrintWriter rpw = new PrintWriter(rfw);
			for (int i = 0; i < denLeft.length; i++) {
				for (int j = 0; j < denLeft[0].length; j++) {
					lpw.print(denLeft[i][j] + " ");
					// rpw.print(denRight[i][j] + " ");
				}
				lpw.println();
				// rpw.println();
			}
			lpw.close();
			lfw.close();
			// rpw.close();
			// rfw.close();
		} catch (IOException e) {
		}
	}

	public double getLocalDensity(double x, double y, double r) {
		int localCellNum = getAgentList(x, y, r).size();
		double localDensity = (double) localCellNum
				/ (double) (Math.PI * r * r);
		return localDensity;
	}

	/**
	 * Transform from original grids to agentspace grids.
	 * 
	 * @param gridx
	 * @param gridy
	 * @return
	 */
	private int[] toSpaceGrid(int gridx, int gridy) {
		int[] spaceGrid = new int[2];
		// mirror agents
		// spaceGrid[0] = gridx + (int) (Global.maxNeighborDis / gridSize);
		// spaceGrid[1] = gridy + (int) (Global.maxNeighborDis / gridSize);
		spaceGrid[0] = gridx;
		spaceGrid[1] = gridy;
		return spaceGrid;
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	private double worldX;

	private double worldY;

	private double gridSize;

	private double pixSize;

	private int numGridX;

	private int numGridY;

	private int numPixX;

	private int numPixY;

	private ArrayList<MyxoAgent> agentList;

	private Grid[][] gridList;

	/**
	 * @return the worldX
	 */
	public double getWorldX() {
		return worldX;
	}

	/**
	 * @param worldX
	 *            the worldX to set
	 */
	public void setWorldX(double worldX) {
		this.worldX = worldX;
	}

	/**
	 * @return the worldY
	 */
	public double getWorldY() {
		return worldY;
	}

	/**
	 * @param worldY
	 *            the worldY to set
	 */
	public void setWorldY(double worldY) {
		this.worldY = worldY;
	}

	/**
	 * @return the gridSize
	 */
	public double getGridSize() {
		return gridSize;
	}

	/**
	 * @param gridSize
	 *            the gridSize to set
	 */
	public void setGridSize(double gridSize) {
		this.gridSize = gridSize;
	}

	/**
	 * @return the numGridX
	 */
	public int getNumGridX() {
		return numGridX;
	}

	/**
	 * @param numGridX
	 *            the numGridX to set
	 */
	public void setNumGridX(int numGridX) {
		this.numGridX = numGridX;
	}

	/**
	 * @return the numGridY
	 */
	public int getNumGridY() {
		return numGridY;
	}

	/**
	 * @param numGridY
	 *            the numGridY to set
	 */
	public void setNumGridY(int numGridY) {
		this.numGridY = numGridY;
	}

	/**
	 * @return the gridList
	 */
	public Grid[][] getGridList() {
		return gridList;
	}

	/**
	 * @param gridList
	 *            the gridList to set
	 */
	public void setGridList(Grid[][] gridList) {
		this.gridList = gridList;
	}

	/**
	 * @param numPixX
	 *            the numPixX to set
	 */
	public void setNumPixX(int numPixX) {
		this.numPixX = numPixX;
	}

	/**
	 * @return the numPixX
	 */
	public int getNumPixX() {
		return numPixX;
	}

	/**
	 * @param numPixY
	 *            the numPixY to set
	 */
	public void setNumPixY(int numPixY) {
		this.numPixY = numPixY;
	}

	/**
	 * @return the numPixY
	 */
	public int getNumPixY() {
		return numPixY;
	}

}
